/* main.c */

#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>

#ifndef cbi
# define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

#ifndef sbi
# define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

uint8_t pins[4] = { 2, 3, 4, 5 };

uint8_t pattern[] = {
  //
  0b10000000,
  0b01000000,
  0b00100000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000,
  0x00,
  //
  0b10010000,
  0b01100000,
  0b10010000,
  0b01100000,
  0x00,
  //
  0b10000000,
  0b11000000,
  0b01100000,
  0b00110000,
  0b00010000,
  0b00000000
};

volatile unsigned long arduino_timer0_overflow_count;

SIGNAL(SIG_OVERFLOW0)
{
   arduino_timer0_overflow_count++;
}

void arduino_initialize()
{
   // this needs to be called before setup() or some functions won't
   // work there
   sei();
	
   // timer 0 is used for millis() and delay()
   arduino_timer0_overflow_count = 0;
   // on the ATmega168, timer 0 is also used for fast hardware pwm
   // (using phase-correct PWM would mean that timer 0 overflowed half as often
   // resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(__AVR_ATmega168__)
   sbi(TCCR0A, WGM01);
   sbi(TCCR0A, WGM00);
#endif  
   // set timer 0 prescale factor to 64
#if defined(__AVR_ATmega168__)
   sbi(TCCR0B, CS01);
   sbi(TCCR0B, CS00);
#else
   sbi(TCCR0, CS01);
   sbi(TCCR0, CS00);
#endif
   // enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
   sbi(TIMSK0, TOIE0);
#else
   sbi(TIMSK, TOIE0);
#endif

   // timers 1 and 2 are used for phase-correct hardware pwm
   // this is better for motors as it ensures an even waveform
   // note, however, that fast pwm mode can achieve a frequency of up
   // 8 MHz (with a 16 MHz clock) at 50% duty cycle
   
   // set timer 1 prescale factor to 64
   sbi(TCCR1B, CS11);
   sbi(TCCR1B, CS10);
   // put timer 1 in 8-bit phase correct pwm mode
   sbi(TCCR1A, WGM10);
   
   // set timer 2 prescale factor to 64
#if defined(__AVR_ATmega168__)
   sbi(TCCR2B, CS22);
#else
   sbi(TCCR2, CS22);
#endif
   // configure timer 2 for phase correct pwm (8-bit)
#if defined(__AVR_ATmega168__)
   sbi(TCCR2A, WGM20);
#else
   sbi(TCCR2, WGM20);
#endif
   
   // set a2d prescale factor to 128
   // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
   // XXX: this will not work properly for other clock speeds, and
   // this code should use F_CPU to determine the prescale factor.
   sbi(ADCSRA, ADPS2);
   sbi(ADCSRA, ADPS1);
   sbi(ADCSRA, ADPS0);
   
   // enable a2d conversions
   sbi(ADCSRA, ADEN);
   
   // the bootloader connects pins 0 and 1 to the USART; disconnect them
   // here so they can be used as normal digital i/o; they will be
   // reconnected in Serial.begin()
#if defined(__AVR_ATmega168__)
   UCSR0B = 0;
#else
   UCSRB = 0;
#endif
}

unsigned long arduino_millis()
{
   // timer 0 increments every 64 cycles, and overflows when it reaches
   // 256.  we would calculate the total number of clock cycles, then
   // divide by the number of clock cycles per millisecond, but this
   // overflows too often.
   //return timer0_overflow_count * 64UL * 256UL / (F_CPU / 1000UL);
   
   // instead find 1/128th the number of clock cycles and divide by
   // 1/128th the number of clock cycles per millisecond
   
   return arduino_timer0_overflow_count * 64UL * 2UL / (F_CPU / 128000UL);
}

void arduino_delay_ms(unsigned long ms)
{
   unsigned long start = arduino_millis();
   while (arduino_millis() - start < ms) {
      // Nothing to do
   }
}

int main(void)
{
   arduino_initialize();

   for (uint8_t i = 0; i < sizeof pins; i++) {
      DDRD |= (1 << pins[i]);
   }

   while (1)
   {
      for (uint8_t i = 0; i < sizeof pattern; i++)
      {
         uint8_t c = pattern[i];
         
         for (uint8_t pin = 0; pin < sizeof pins; pin++) {
            if (c & (1 << (7 - pin))) {
               PORTD ^= (1 << pins[pin]);
            }
         }

         arduino_delay_ms(250);

         for (uint8_t pin = 0; pin < sizeof pins; pin++) {
            if (c & (1 << (7 - pin))) {
               PORTD ^= (1 << pins[pin]);
            }
         }
      }
   }
   
   return 0;
}
